/*     */ package com.zimbra.cs.security.sasl;
/*     */ 
/*     */ import com.zimbra.common.account.Key.AccountBy;
/*     */ import com.zimbra.common.localconfig.KnownKey;
/*     */ import com.zimbra.common.localconfig.LC;
/*     */ import com.zimbra.common.service.ServiceException;
/*     */ import com.zimbra.common.util.Log;
/*     */ import com.zimbra.common.util.ZimbraLog;
/*     */ import com.zimbra.cs.account.Account;
/*     */ import com.zimbra.cs.account.Provisioning;
/*     */ import com.zimbra.cs.account.Server;
/*     */ import com.zimbra.cs.account.auth.AuthContext.Protocol;
/*     */ import com.zimbra.cs.security.kerberos.Krb5Keytab;
/*     */ import java.io.IOException;
/*     */ import java.io.InputStream;
/*     */ import java.io.OutputStream;
/*     */ import java.io.PrintStream;
/*     */ import java.net.InetAddress;
/*     */ import java.security.PrivilegedActionException;
/*     */ import java.security.PrivilegedExceptionAction;
/*     */ import java.util.HashMap;
/*     */ import java.util.List;
/*     */ import java.util.Map;
/*     */ import java.util.Set;
/*     */ import javax.security.auth.Subject;
/*     */ import javax.security.auth.callback.Callback;
/*     */ import javax.security.auth.callback.CallbackHandler;
/*     */ import javax.security.auth.callback.UnsupportedCallbackException;
/*     */ import javax.security.auth.kerberos.KerberosKey;
/*     */ import javax.security.auth.kerberos.KerberosPrincipal;
/*     */ import javax.security.sasl.AuthorizeCallback;
/*     */ import javax.security.sasl.Sasl;
/*     */ import javax.security.sasl.SaslException;
/*     */ import javax.security.sasl.SaslServer;
/*     */ import org.apache.commons.codec.binary.Base64;
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ public class GssAuthenticator
/*     */   extends Authenticator
/*     */ {
/*     */   private SaslServer mSaslServer;
/*     */   private boolean mEncryptionEnabled;
/*     */   private static final String QOP_AUTH = "auth";
/*     */   private static final String QOP_AUTH_INT = "auth-int";
/*     */   private static final String QOP_AUTH_CONF = "auth-conf";
/*     */   private static final int MAX_RECEIVE_SIZE = 4096;
/*     */   private static final int MAX_SEND_SIZE = 4096;
/*     */   public static final String KRB5_DEBUG_ENABLED_PROP = "ZimbraKrb5DebugEnabled";
/*     */   private static final boolean DEBUG;
/*     */   public static final String MECHANISM = "GSSAPI";
/*     */   private static final Boolean GSS_ENABLED;
/*     */   private static final Map<String, String> ENCRYPTION_PROPS;
/*     */   
/*     */   static
/*     */   {
/*  63 */     DEBUG = (LC.krb5_debug_enabled.booleanValue()) || (Boolean.getBoolean("ZimbraKrb5DebugEnabled"));
/*     */     
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*  69 */     GSS_ENABLED = Boolean.valueOf(Boolean.getBoolean("ZimbraGssEnabled"));
/*     */     
/*     */ 
/*  72 */     ENCRYPTION_PROPS = new HashMap();
/*     */     
/*     */ 
/*  75 */     ENCRYPTION_PROPS.put("javax.security.sasl.qop", "auth,auth-int,auth-conf");
/*  76 */     ENCRYPTION_PROPS.put("javax.security.sasl.maxbuffer", String.valueOf(4096));
/*  77 */     ENCRYPTION_PROPS.put("javax.security.sasl.rawsendsize", String.valueOf(4096));
/*  78 */     if (DEBUG) {
/*  79 */       System.setProperty("sun.security.krb5.debug", "true");
/*  80 */       System.setProperty("sun.security.jgss.debug", "true");
/*     */     }
/*     */   }
/*     */   
/*     */   public GssAuthenticator(AuthenticatorUser user) {
/*  85 */     super("GSSAPI", user);
/*     */   }
/*     */   
/*     */ 
/*     */   protected boolean isSupported()
/*     */   {
/*  91 */     if ((!GSS_ENABLED.booleanValue()) && (!this.mAuthUser.isGssapiAvailable())) {
/*  92 */       return false;
/*     */     }
/*     */     try
/*     */     {
/*  96 */       return (this.mAuthUser.isSSLEnabled()) || (!Provisioning.getInstance().getLocalServer().getBooleanAttr("zimbraSaslGssapiRequiresTls", false));
/*     */     }
/*     */     catch (ServiceException e) {
/*  99 */       ZimbraLog.security.warn("could not determine whether TLS encryption is required for GSSAPI auth; defaulting to FALSE", e); }
/* 100 */     return false;
/*     */   }
/*     */   
/*     */   public boolean initialize()
/*     */     throws IOException
/*     */   {
/* 106 */     Krb5Keytab keytab = getKeytab(LC.krb5_keytab.value());
/* 107 */     if (keytab == null) {
/* 108 */       sendFailed("mechanism not supported");
/* 109 */       return false;
/*     */     }
/* 111 */     debug("keytab file = %s", new Object[] { keytab.getFile() });
/*     */     String host;
/*     */     final String host;
/* 114 */     if (LC.krb5_service_principal_from_interface_address.booleanValue()) {
/* 115 */       String localSocketHostname = this.localAddress.getCanonicalHostName().toLowerCase();
/* 116 */       if ((localSocketHostname.length() == 0) || (Character.isDigit(localSocketHostname.charAt(0))))
/* 117 */         localSocketHostname = LC.zimbra_server_hostname.value();
/* 118 */       host = localSocketHostname;
/*     */     } else {
/* 120 */       host = LC.zimbra_server_hostname.value();
/*     */     }
/*     */     
/* 123 */     KerberosPrincipal kp = new KerberosPrincipal(getProtocol() + '/' + host);
/* 124 */     debug("kerberos principal = %s", new Object[] { kp });
/* 125 */     Subject subject = getSubject(keytab, kp);
/* 126 */     if (subject == null) {
/* 127 */       sendFailed();
/* 128 */       return false;
/*     */     }
/* 130 */     debug("subject = %s", new Object[] { subject });
/*     */     
/* 132 */     final Map<String, String> props = getSaslProperties();
/* 133 */     if ((DEBUG) && (props != null)) {
/* 134 */       String qop = (String)props.get("javax.security.sasl.qop");
/* 135 */       debug("Sent QOP = " + (qop != null ? qop : "auth"), new Object[0]);
/*     */     }
/*     */     try
/*     */     {
/* 139 */       this.mSaslServer = ((SaslServer)Subject.doAs(subject, new PrivilegedExceptionAction()
/*     */       {
/*     */         public Object run() throws SaslException {
/* 142 */           return Sasl.createSaslServer(GssAuthenticator.this.getMechanism(), GssAuthenticator.this.getProtocol(), host, props, new GssAuthenticator.GssCallbackHandler(GssAuthenticator.this));
/*     */         }
/*     */       }));
/*     */     } catch (PrivilegedActionException e) {
/* 146 */       sendFailed();
/* 147 */       getLog().warn("Could not create SaslServer", e.getCause());
/* 148 */       return false;
/*     */     }
/* 150 */     return true;
/*     */   }
/*     */   
/*     */   private Krb5Keytab getKeytab(String path)
/*     */   {
/*     */     try {
/* 156 */       return Krb5Keytab.getInstance(path);
/*     */     } catch (IOException e) {
/* 158 */       getLog().warn("Keytab file '" + path + "' not found"); }
/* 159 */     return null;
/*     */   }
/*     */   
/*     */   private Subject getSubject(Krb5Keytab keytab, KerberosPrincipal kp)
/*     */     throws IOException
/*     */   {
/* 165 */     List<KerberosKey> keys = keytab.getKeys(kp);
/* 166 */     if (keys == null) {
/* 167 */       getLog().warn("Key not found in keystore for service principal '" + kp + "'");
/* 168 */       return null;
/*     */     }
/* 170 */     Subject subject = new Subject();
/* 171 */     subject.getPrincipals().add(kp);
/* 172 */     subject.getPrivateCredentials().addAll(keys);
/* 173 */     return subject;
/*     */   }
/*     */   
/*     */   public void handle(byte[] data) throws IOException
/*     */   {
/* 178 */     if (isComplete()) {
/* 179 */       throw new IllegalStateException("Authentication already completed");
/*     */     }
/*     */     byte[] bytes;
/*     */     try
/*     */     {
/* 184 */       bytes = this.mSaslServer.evaluateResponse(data);
/*     */     } catch (SaslException e) {
/* 186 */       getLog().warn("SaslServer.evaluateResponse() failed", e);
/*     */       
/* 188 */       if (!isComplete()) sendBadRequest();
/* 189 */       return;
/*     */     }
/*     */     
/* 192 */     if (!isComplete()) {
/* 193 */       assert (!this.mSaslServer.isComplete());
/* 194 */       String s = new String(Base64.encodeBase64(bytes), "US-ASCII");
/* 195 */       sendContinuation(s);
/* 196 */       return;
/*     */     }
/*     */     
/* 199 */     assert (this.mSaslServer.isComplete());
/* 200 */     if (DEBUG) { dumpNegotiatedProperties();
/*     */     }
/* 202 */     if (!isAuthenticated()) {
/* 203 */       debug("Authentication failed", new Object[0]);
/* 204 */       dispose();
/* 205 */       return;
/*     */     }
/*     */     
/* 208 */     String qop = (String)this.mSaslServer.getNegotiatedProperty("javax.security.sasl.qop");
/* 209 */     if (("auth-int".equals(qop)) || ("auth-conf".equals(qop))) {
/* 210 */       debug("SASL encryption enabled (%s)", new Object[] { qop });
/* 211 */       this.mEncryptionEnabled = true;
/*     */     } else {
/* 213 */       dispose();
/*     */     }
/*     */   }
/*     */   
/*     */ 
/*     */   public Account authenticate(String username, String principal, String unused, AuthContext.Protocol protocol, String origRemoteIp, String remoteIp, String userAgent)
/*     */     throws ServiceException
/*     */   {
/* 221 */     Provisioning prov = Provisioning.getInstance();
/* 222 */     Account authAccount = prov.get(Key.AccountBy.krb5Principal, principal);
/* 223 */     if (authAccount == null) {
/* 224 */       ZimbraLog.account.warn("authentication failed (no account associated with Kerberos principal " + principal + ')');
/* 225 */       return null;
/*     */     }
/*     */     
/* 228 */     Account targetAcct = authorize(authAccount, username, true);
/* 229 */     if (targetAcct != null)
/* 230 */       prov.accountAuthed(authAccount);
/* 231 */     return targetAcct;
/*     */   }
/*     */   
/*     */   private Map<String, String> getSaslProperties()
/*     */   {
/* 236 */     return this.mAuthUser.isSSLEnabled() ? null : ENCRYPTION_PROPS;
/*     */   }
/*     */   
/*     */   public boolean isEncryptionEnabled()
/*     */   {
/* 241 */     return this.mEncryptionEnabled;
/*     */   }
/*     */   
/*     */   public InputStream unwrap(InputStream is)
/*     */   {
/* 246 */     return new SaslInputStream(is, this.mSaslServer);
/*     */   }
/*     */   
/*     */   public OutputStream wrap(OutputStream os)
/*     */   {
/* 251 */     return new SaslOutputStream(os, this.mSaslServer);
/*     */   }
/*     */   
/*     */   public SaslServer getSaslServer()
/*     */   {
/* 256 */     return this.mSaslServer;
/*     */   }
/*     */   
/*     */   public void dispose()
/*     */   {
/* 261 */     debug("dispose called", new Object[0]);
/*     */     try {
/* 263 */       this.mSaslServer.dispose();
/*     */     } catch (SaslException e) {
/* 265 */       getLog().warn("SaslServer.dispose() failed", e);
/*     */     }
/*     */   }
/*     */   
/*     */   private class GssCallbackHandler implements CallbackHandler
/*     */   {
/*     */     GssCallbackHandler() {}
/*     */     
/*     */     public void handle(Callback[] cbs) throws IOException, UnsupportedCallbackException
/*     */     {
/* 275 */       if ((cbs == null) || (cbs.length != 1)) {
/* 276 */         throw new IOException("Bad callback");
/*     */       }
/* 278 */       if (!(cbs[0] instanceof AuthorizeCallback)) {
/* 279 */         throw new UnsupportedCallbackException(cbs[0]);
/*     */       }
/* 281 */       AuthorizeCallback cb = (AuthorizeCallback)cbs[0];
/* 282 */       GssAuthenticator.debug("gss authorization_id = %s", new Object[] { cb.getAuthorizationID() });
/* 283 */       GssAuthenticator.debug("gss authentication_id = %s", new Object[] { cb.getAuthenticationID() });
/* 284 */       cb.setAuthorized(GssAuthenticator.this.authenticate(cb.getAuthorizationID(), cb.getAuthenticationID(), null));
/*     */     }
/*     */   }
/*     */   
/*     */   private void dumpNegotiatedProperties()
/*     */   {
/* 290 */     pp("QOP", "javax.security.sasl.qop");
/* 291 */     pp("MAX_BUFFER", "javax.security.sasl.maxbuffer");
/* 292 */     pp("MAX_RECEIVE_SIZE", "javax.security.sasl.rawsendsize");
/* 293 */     pp("STRENGTH", "javax.security.sasl.strength");
/*     */   }
/*     */   
/*     */   private void pp(String printName, String propName) {
/* 297 */     Object obj = this.mSaslServer.getNegotiatedProperty(propName);
/* 298 */     if (obj != null)
/* 299 */       debug("Negotiated property %s = %s", new Object[] { printName, obj });
/*     */   }
/*     */   
/*     */   static void debug(String format, Object... args) {
/* 303 */     if (DEBUG) {
/* 304 */       System.out.printf("[DEBUG GssAuthenticator] " + format + "\n", args);
/*     */     }
/*     */   }
/*     */ }


/* Location:              /home/mint/zimbrastore.jar!/com/zimbra/cs/security/sasl/GssAuthenticator.class
 * Java compiler version: 7 (51.0)
 * JD-Core Version:       0.7.1
 */